#include "stdafx.h"

#include "PC2DataStore.h"

#include "BankAccountStoreExecutor.h"


namespace PC2
{


BankAccountStoreExecutor::BankAccountStoreExecutor( std::string const& aName, PC2DataStore* apStore )
	: m_Name(aName)
	, m_pStore(apStore)
	, m_AccountMap()
{
	ReloadAllAccounts();
}

BankAccountStoreExecutor::~BankAccountStoreExecutor()
{

}

void BankAccountStoreExecutor::DumpAllAcount(std::vector<BankAccount>* apVec)
{
	for (auto it = m_AccountMap.begin(); it != m_AccountMap.end(); it++)
	{
		apVec->push_back(it->second);
	}
}

void BankAccountStoreExecutor::SetAccountValueAndStore( std::string const& aName, uint32_t aValue )
{
	std::string lKey = AccountToStringID(aName.c_str());

	auto it = m_AccountMap.find(lKey);
	if(m_AccountMap.end() == it)
	{
		BankAccount lAccount;
		strcpy(lAccount.m_Name, aName.c_str());
		lAccount.m_Value = aValue;

		m_AccountMap[lKey] = lAccount;
	}
	else
	{
		it->second.m_Value = aValue;
	}

	BankAccount* lpAccount = &m_AccountMap.at(lKey);

	leveldb::WriteBatch lBatch;
	lBatch.Put(lKey, leveldb::Slice((char*)&lpAccount->m_Value, sizeof(uint32_t)));

	m_pStore->CommitBatch(&lBatch);
}

void BankAccountStoreExecutor::ReloadAllAccounts()
{
	leveldb::Iterator* lpIt = m_pStore->CreateIterator();

	for (lpIt->SeekToFirst(); lpIt->Valid(); lpIt->Next())
	{
		BankAccount lBankAccount;
		uint32_t lValue;
		std::string lKeyString = lpIt->key().ToString();
		if(lpIt->key().starts_with("_ACC_"))
		{
			StringIDToAccount(&lKeyString, &lBankAccount);
			lValue = *(uint32_t*)lpIt->value().data();
			lBankAccount.m_Value = lValue;

			m_AccountMap[lKeyString] = lBankAccount;
		}

	}

	delete lpIt;
}

bool BankAccountStoreExecutor::Prepare( uint32_t aTransId, char const* apData, uint32_t aSize, leveldb::WriteBatch* apBatch )
{
	BankTransaction* lpTrans = (BankTransaction*)apData;
	std::string lNameKey = AccountToStringID(lpTrans->m_Name);

	bool isFrom;

	if(lpTrans->m_BankFrom == m_Name)
	{
		isFrom = true;
	}
	else if(lpTrans->m_BankTo == m_Name)
	{
		isFrom = false;
	}
	else
	{
		return false;
	}

	BankAccount* lpAccount = NULL;
	auto it = m_AccountMap.find(lNameKey);
	if(m_AccountMap.end() != it)
	{
		lpAccount = &it->second;
	}
	else
	{
		LOG("prepare reject, cannot find account name %s\n", lpTrans->m_Name);
		return false;
	}

	if(isFrom && lpAccount->m_Value < lpTrans->m_Value)
	{
		LOG("prepare reject, not enough, name %s bank_v %u trans_v %u\n", 
			lpTrans->m_Name, lpAccount->m_Value, lpTrans->m_Value);
		return false;
	}
	else
	{
		LOG("prepare ok, name %s current bank_v %u\n", 
			lpTrans->m_Name, lpAccount->m_Value);

		return true;
	}
}

void BankAccountStoreExecutor::Commit( uint32_t aTransId, char const* apData, uint32_t aSize, leveldb::WriteBatch* apBatch )
{
	BankTransaction* lpTrans = (BankTransaction*)apData;
	std::string lNameKey = AccountToStringID(lpTrans->m_Name);

	bool isFrom;

	if(lpTrans->m_BankFrom == m_Name)
	{
		isFrom = true;
	}
	else if(lpTrans->m_BankTo == m_Name)
	{
		isFrom = false;
	}
	else
	{
		return;
	}

	BankAccount* lpAccount = NULL;
	auto it = m_AccountMap.find(lNameKey);
	if(m_AccountMap.end() != it)
	{
		lpAccount = &it->second;
	}
	else
	{
		LOG("commit cannot find account name %s\n", lpTrans->m_Name);
		return;
	}

	if(isFrom && lpAccount->m_Value < lpTrans->m_Value)
	{
		LOG("commit error, not enough, name %s bank_v %u trans_v %u\n", 
			lpTrans->m_Name, lpAccount->m_Value, lpTrans->m_Value);
		return;
	}

	if(isFrom)
	{
		lpAccount->m_Value -= lpTrans->m_Value;
	}
	else
	{
		lpAccount->m_Value += lpTrans->m_Value;
	}

	apBatch->Put(lNameKey, leveldb::Slice((char*)&lpAccount->m_Value, sizeof(uint32_t)));


	LOG("commit ok\n");
	LOG("name %s new bank_v %u\n", lpTrans->m_Name, lpAccount->m_Value);

}

void BankAccountStoreExecutor::Abort( uint32_t aTransId, char const* apData, uint32_t aSize, leveldb::WriteBatch* apBatch )
{
	LOG("abort ok\n");
	return;
}

void BankAccountStoreExecutor::StringIDToAccount( std::string* apKey, BankAccount* apAccount )
{
	sscanf(apKey->c_str() + 5, "%s", apAccount->m_Name);
}

std::string BankAccountStoreExecutor::AccountToStringID( BankAccount* apAccount )
{
	return AccountToStringID(apAccount->m_Name);
}

std::string BankAccountStoreExecutor::AccountToStringID( char const* apName )
{
	char lBuffer[256];
	sprintf(lBuffer, "_ACC_%s", apName);

	return std::string(lBuffer);
}






}